package com.atguigu.paymentdemo.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.paymentdemo.config.MyWXPayConfig;
import com.atguigu.paymentdemo.entity.OrderInfo;
import com.atguigu.paymentdemo.entity.PaymentInfo;
import com.atguigu.paymentdemo.entity.Product;
import com.atguigu.paymentdemo.enums.OrderStatus;
import com.atguigu.paymentdemo.service.OrderInfoService;
import com.atguigu.paymentdemo.service.PaymentInfoService;
import com.atguigu.paymentdemo.service.WxPayService;
import com.atguigu.paymentdemo.util.WXPayUtils;
import com.atguigu.paymentdemo.vo.R;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.wechat.pay.contrib.apache.httpclient.auth.CertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@CrossOrigin
@RestController
@RequestMapping("wxPay")
@Api(tags="网站微信支付API")
@Slf4j
public class WxPayController {

    @Resource
    private WxPayService wxPayService;

    @Autowired
    private MyWXPayConfig wxPayConfig;

    @Autowired
    private OrderInfoService orderInfoService;

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private com.atguigu.paymentdemo.config.WXPayUtils wxPayUtils;

    @Autowired
    private CertificatesVerifier verifier;


    /**
     * 微信支付 生成二维码
     *
     * @param product
     * @return
     */
    @GetMapping("/pay")
    public R wxPay(HttpServletRequest request,Product product) throws Exception{
        Cookie[] cookies = request.getCookies();
        String userId = "oFy9w57dqci5jcOrYRyhdMLbd_zA";
        if (null != cookies) {
            for (Cookie cookie : cookies) {
                if("openid".equals(cookie.getName())) {
                    userId = cookie.getValue();
                }
            }
        }
        if(StringUtils.isBlank(userId)) {
            throw new RuntimeException("用户未登录");
        }
        //调用统一下单API,生成订单
        OrderInfo orderInfo = orderInfoService.createOrderByProdectId(product,userId);
//        WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
//        wxPayUnifiedOrderRequest.setAppid(wxPayConfig.getAppID());
//        String body = orderInfo.getTitle();
//        body = body.length() > 40 ? body.substring(0,39) : body;
//        wxPayUnifiedOrderRequest.setBody(body);
//        wxPayUnifiedOrderRequest.setOutTradeNo(orderInfo.getOrderNo());
//        wxPayUnifiedOrderRequest.setTotalFee(orderInfo.getTotalFee());
//        wxPayUnifiedOrderRequest.setTradeType("JSAPI");
//        wxPayUnifiedOrderRequest.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
//        wxPayUnifiedOrderRequest.setSpbillCreateIp("127.0.0.1");
//        wxPayUnifiedOrderRequest.setOpenid(userId);
//        com.github.binarywang.wxpay.service.WxPayService wxPayService = WxPayConfiguration.getPayService();
//        Object order = wxPayService.createOrder(wxPayUnifiedOrderRequest);
//        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(order);
//        log.info("返回结果----"+stringObjectMap);
//        return R.ok().setData(stringObjectMap);

//        WxPayData packageParams = new WxPayData();
//        packageParams.put("appid", wxPayConfig.getAppID());
//        packageParams.put("mch_id", wxPayConfig.getMchID());
//        String currTime = PayCommonUtil.getCurrTime();
//        String strTime = currTime.substring(8, currTime.length());
//        String strRandom = PayCommonUtil.buildRandom(4) + "";
//        // 随机字符串
//        String nonce_str = strTime + strRandom;
//        packageParams.put("nonce_str", nonce_str);
//        String body = orderInfo.getTitle();
//        body = body.length() > 40 ? body.substring(0,39) : body;
//        packageParams.put("body", body);
//        packageParams.put("out_trade_no", orderInfo.getOrderNo());
//        packageParams.put("total_fee", orderInfo.getTotalFee().toString());
//        packageParams.put("spbill_create_ip", "127.0.0.1");
//        packageParams.put("notify_url", wxPayConfig.getPayNotifyUrl());
//        packageParams.put("trade_type", "JSAPI");
//        String sign = packageParams.MakeSign();
//        packageParams.put("sign", sign);
//        WxPayData resXml= WxPayApi.UnifiedOrder(packageParams, 20);
//        String requestXML = packageParams.ToXml();
//        log.info("请求xml：：：：" + requestXML);
//        log.info("得到xml：：：：" + resXml.toString());
//        // String return_code = (String) map.get("return_code");
//        // String prepay_id = (String) map.get("prepay_id");
//        String urlCode = (String) resXml.GetValue("code_url");
        //Map<String,Object> map= wxPayService.nativePay(orderInfo);

        JSONObject jsonObject = buildWxJsApiPayJson(wxPayConfig.getAppID(), wxPayConfig.getMchID(), orderInfo.getTitle(),
                orderInfo.getOrderNo(), wxPayConfig.getPayNotifyUrl(), orderInfo.getTotalFee(),
                userId);
        Map result = wxPayUtils.doPostWexinV3("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi", jsonObject.toJSONString());
        log.info("返回参数：====="+result);
        return  R.ok().setData(result);
    }

    /**
     * 构造微信JsApi付款的json
     * @param appid
     * @param mchid
     * @param description
     * @param out_trade_no
     * @param notify_url
     * @param amount
     * @return
     */
    public JSONObject buildWxJsApiPayJson(String appid , String mchid ,
                                          String description ,
                                          String out_trade_no ,
                                          String notify_url ,
                                          BigDecimal amount,
                                          String openId){

        //订单金额json
        JSONObject amountJson = new JSONObject();
        amountJson.put("total",amount);
        amountJson.put("currency","CNY");

        //支付者json
        JSONObject payerJson = new JSONObject();
        payerJson.put("openid",openId);

        //基础信息json
        JSONObject json = new JSONObject();
        json.put("appid",appid);
        json.put("mchid",mchid);
        json.put("description",description);
        json.put("out_trade_no",out_trade_no);
        json.put("notify_url",notify_url);
        json.put("amount",amountJson);
        json.put("payer",payerJson);
        return json;
    }

    @ResponseBody
    @RequestMapping("/callback1")
    public String notify_url(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("支付信息回调中");
        String notifyData = "";
        try{

            InputStream inputStream = request.getInputStream();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[2048];
            int len = 0;
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }

            notifyData = new String(outputStream.toByteArray(), "utf-8");// 支付结果通知的xml格式数据

            //关闭流
            outputStream.close();
            inputStream.close();
            log.info("获取xml格式数据成功：\n"+notifyData);
        }catch (Exception e){
            log.info("解析回调数据发生异常");
        }

        MyWXPayConfig config = new MyWXPayConfig();//配置类

        Map<String, String> notifyMap = WXPayUtils.xmlToMap(notifyData);  // 转换成map
        log.info("notifyMap = "+notifyMap);
        log.info("sign = "+notifyMap.get("sign"));

        if (WXPayUtils.isCorrectSign(WXPayUtils.getSortedMap(notifyMap),config.getKey())) {//HMACSHA256签名验证方法
            // 签名正确
            // 进行处理。
            // 注意特殊情况：订单已经退款，但收到了支付结果成功的通知，不应把商户侧订单状态从退款改成支付成功

            String out_trade_no = notifyMap.get("out_trade_no");//获取商户订单号
            String transaction_id = notifyMap.get("transaction_id");//微信支付订单号
            String amount = notifyMap.get("total_fee");


            //处理标记付款成功
            OrderInfo order = orderInfoService.findOrderByOrderNo(out_trade_no);
            order.setOrderStatus(OrderStatus.SUCCESS.getType());
            orderInfoService.updateById(order);
            PaymentInfo info = new PaymentInfo();
            info.setOrderNo(order.getOrderNo());
            info.setContent(notifyData);
            info.setPayerTotal(amount != null ?Integer.parseInt(amount):null);
            info.setTradeState(OrderStatus.SUCCESS.getType());
            paymentInfoService.save(info);//保存到数据库
            log.info("回调验证成功 业务代码已经处理");

            //返回给微信的数据 标志成功
            Map<String,String> map = new HashMap<String, String>();
            map.put("return_code","SUCCESS");//SUCCESS表示商户接收通知成功并校验成功
            map.put("return_msg","OK");

            log.info("验签成功");

            return WXPayUtils.mapToXml(map);
        }
        else {
            // 签名错误，如果数据里没有sign字段，也认为是签名错误
            //返回给微信的数据 标志失败
            Map<String,String> map = new HashMap<String, String>();
            map.put("return_code","FAIL");
            map.put("return_msg","NO");

            log.info("验签失败");
            return WXPayUtils.mapToXml(map);
        }
    }
    @ApiOperation(value = "支付回调")
    @PostMapping("/callback")
    public R notifyOrder(HttpServletRequest request, HttpServletResponse response) throws WxPayException {
        //获取报文
        String body = getRequestBody(request);

        //随机串
        String nonceStr = request.getHeader("Wechatpay-Nonce");

        //微信传递过来的签名
        String signature = request.getHeader("Wechatpay-Signature");

        //证书序列号（微信平台）
        String serialNo = request.getHeader("Wechatpay-Serial");

        //时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");



        //构造签名串

        //应答时间戳\n
        //应答随机串\n
        //应答报文主体\n
        String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));

        Map<String, Object> map = new HashMap<>(2);
        try {
            //验证签名是否通过
            boolean result = verifiedSign(serialNo, signStr, signature);

            if(result){
                //解密数据
                String plainBody = decryptBody(body);
                log.info("解密后的明文:{}",plainBody);

                Map<String, String> paramsMap = convertWechatPayMsgToMap(plainBody);
                //处理业务逻辑 TODO

                //获取商户订单号
                String out_trade_no = paramsMap.get("out_trade_no");

                //处理标记付款成功
                OrderInfo order = orderInfoService.findOrderByOrderNo(out_trade_no);
                order.setOrderStatus(OrderStatus.SUCCESS.getType());
                orderInfoService.updateById(order);

                //响应微信
                map.put("code", "SUCCESS");
                map.put("message", "成功");
                return R.ok().setData(map);
            }



        } catch (Exception e) {
            log.error("微信支付回调异常:{}", e);
        }

        return R.error();
    }



    /**
     * 转换body为map
     * @param plainBody
     * @return
     */
    private Map<String,String> convertWechatPayMsgToMap(String plainBody){

        Map<String,String> paramsMap = new HashMap<>(2);

        JSONObject jsonObject = JSONObject.parseObject(plainBody);

        //商户订单号
        paramsMap.put("out_trade_no",jsonObject.getString("out_trade_no"));

        //交易状态
        paramsMap.put("trade_state",jsonObject.getString("trade_state"));

        //附加数据
        //paramsMap.put("account_no",jsonObject.getJSONObject("attach").getString("accountNo"));

        return paramsMap;

    }



    /**
     * 解密body的密文
     *
     * "resource": {
     *         "original_type": "transaction",
     *         "algorithm": "AEAD_AES_256_GCM",
     *         "ciphertext": "",
     *         "associated_data": "",
     *         "nonce": ""
     *     }
     *
     * @param body
     * @return
     */
    private String decryptBody(String body) throws UnsupportedEncodingException, GeneralSecurityException {

        AesUtil aesUtil = new AesUtil(wxPayConfig.getKey().getBytes("utf-8"));

        JSONObject object = JSONObject.parseObject(body);
        JSONObject resource = object.getJSONObject("resource");
        String ciphertext = resource.getString("ciphertext");
        String associatedData = resource.getString("associated_data");
        String nonce = resource.getString("nonce");

        return aesUtil.decryptToString(associatedData.getBytes("utf-8"),nonce.getBytes("utf-8"),ciphertext);

    }



    /**
     * 验证签名
     *
     * @param serialNo  微信平台-证书序列号
     * @param signStr   自己组装的签名串
     * @param signature 微信返回的签名
     * @return
     * @throws UnsupportedEncodingException
     */
    private boolean verifiedSign(String serialNo, String signStr, String signature) throws UnsupportedEncodingException {
        return verifier.verify(serialNo, signStr.getBytes("utf-8"), signature);
    }


    /**
     * 读取请求数据流
     *
     * @param request
     * @return
     */
    private String getRequestBody(HttpServletRequest request) {

        StringBuffer sb = new StringBuffer();

        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        ) {
            String line;

            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }

        } catch (IOException e) {
            log.error("读取数据流异常:{}", e);
        }

        return sb.toString();

    }

}
